﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Linq.Expressions;

namespace IntelliLance.Math.Parser
{
    /// <summary>
    /// Defines properties for an arithmetic function.
    /// </summary>
    public class FunctionDefinition
    {
        private readonly StringComparer _comparer;
        private readonly MethodInfo _method;
        private System.Linq.Expressions.Expression _expression;

        /// <summary>
        /// Gets a string that represents the name of the underlying function.
        /// </summary>
        /// <remarks>This property returns the exact name of the function encountered by the parser.</remarks>
        /// <value>A string that represents the name of the current FunctionDefinition instance.</value>
        public string Name
        {
            get;
            private set; 
        }

        /// <summary>
        /// Gets a collection of Token objects associated with their names.
        /// </summary>
        /// <remarks>If the value of the Method property is null, this property will return null.</remarks>
        /// <value>An instance of Dictionary(of string, ParameterInfo).</value>
        public Dictionary<string, ParameterInfo> Parameters 
        {
            get;
            private set; 
        }

        /// <summary>
        /// Gets an Expression instance for the underlying function.
        /// </summary>
        /// <remarks>This property returns the same Expression instance that's passed to the constructor of FunctionDefinition.</remarks>
        /// <value>An instance of the System.Linq.Expressions.Expression that represent the current function definition.</value>
        public Expression Expression
        {
            get 
            {
                return _expression;
            }
        }

        /// <summary>
        /// Gets an instance of a MethodInfo class that represents the underlying function method.
        /// </summary>
        /// <remarks>This property return the MethodInfo object that's passed t the constructor of this class. 
        /// This property could return null if the Expression property isn't null.</remarks>
        /// <value>An instance of a MethodInfo class.</value>
        public MethodInfo Method
        {
            get
            { 
                return _method; 
            }
        }

        /// <summary>
        /// Initialize a new instance of the FunctionDefition class with the specified name, and a reference to a Expression instance, and a reference to a StringComparer instance.
        /// </summary>
        internal FunctionDefinition(string name, Expression expression, StringComparer comparer)
        {
            if (name == null)
                ThrowHelpers.ThrowNullArgument("name");

            if (expression == null)
                ThrowHelpers.ThrowNullArgument("expression");

            if (comparer == null)
                ThrowHelpers.ThrowNullArgument("comparer");

            this.Name = name;
            this._expression = expression;
            this._comparer = comparer;
        }

        /// <summary>
        /// Initialize a new instance of the FunctionDefition class with the specified name, and a reference to a Delegate instance, and a reference to a StringComparer instance.
        /// </summary>
        internal FunctionDefinition(string name, MethodInfo method, StringComparer comparer) 
        {
            if (name == null)
                ThrowHelpers.ThrowNullArgument("name");

            if (method == null)
                ThrowHelpers.ThrowNullArgument("method");

            if (comparer == null)
                ThrowHelpers.ThrowNullArgument("comparer");

            this.Name = name;
            this._method = method;
            this._comparer = comparer;
            this.Parameters = new Dictionary<string, ParameterInfo>(_comparer);
            foreach (var item in method.GetParameters())
            {
                this.Parameters[item.Name] = item;
            }

        }

        /// <summary>
        /// Initialize a new instance of the FunctionDefition class with the specified name, and a reference to a MethodInfo
        /// instance, and a reference to a StringComparer instance.
        /// </summary>
        internal FunctionDefinition(string name, Delegate method, StringComparer comparer) : this(name, method.Method, comparer) { }

        /// <summary>
        /// Initialize a new instance of the FunctionDefition class with the specified name, and a reference to a StringComparer instance.
        /// </summary>
        /// <remarks>This constructor should  not be called alone.</remarks>
        internal FunctionDefinition(string name, MathParserOptions options)
	    {
            if (name == null)
                ThrowHelpers.ThrowNullArgument("name");
            this.Name = name;
            this._comparer = StringComparer.Create(CultureInfo.InvariantCulture, options.IgnoreCase);
	    }

        /// <summary>
        /// Returns true if the current Function name and parameters match the passed FunctionToken object.
        /// </summary>
        /// <returns>True if the passed-in FunctionToken object has the same name, and parameters count, otherwise, it returns false.</returns>
        internal bool Match(FunctionToken function)
        {
            return _comparer.Equals(function.Value, this.Name)
                && function.ParametersCount == this.Parameters.Count;
        }

    }
}
